#!/usr/bin/perl -w
# distribute-keys.pm
# Copyright (C) 2006  Stephane Alnet
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 

#
# For more information: http://carrierclass.net/
#
package CCNCore::Provision::distribute_keys;
use strict; use warnings;
use CCNCore::Provision::Base;
use base qw(CCNCore::Provision::Base);
use CCN::Text;
use CCNCore::Home;
use CCNCore::Scripting;
use CCNCore::IPC::Spool;

sub run()
{
    my $self = shift;
    return unless $self->p->is_manager;

    my $manager_ip = $self->primary_ip($self->p->server_pk);
    
    if( $self->bootstrapping )
    {
        CCN::Scripting::exec_command("mkdir -p ".CCNCore::Home::config."/system/$manager_ip/ && cp /root/.ssh/id_rsa.pub ".CCNCore::Home::config."/system/$manager_ip/id_rsa.pub");
    }

    # This should be it. distribute_keys.pl is called from the GUI or triggered via spool.pl.
}

# Note: this is not a regular "commit" (as used by COMMIT_MODULE)
# This version is used by CCNCore::spool::distribute_keys.
# commit [<server-pk>] : optional server_pk (otherwise, do it for all servers)

sub commit
{
    our $self = shift;

    # Only the manager server distribute keys within a system.
    return unless $self->p->is_manager;
    
    my $manager_pk = $self->p->manager;

    my $t = shift;

    # Optional parameter: pk of the server to update
    my $server_pk = shift;
    
    my $sql_where;
    my $sql_param;
    if( defined $server_pk )
    {
        $sql_where = q(pk = ?);
        $sql_param = $server_pk;
    }
    else
    {
        $sql_where = q(system = ?);
        $sql_param = $self->p->system_pk;
    }

    my $count = 0;
    
    my $spool = new CCNCore::IPC::Spool;

    $self->sql_do_for( q(SELECT pk, cdn_downstream_server, cp_member FROM server WHERE ).$sql_where, $sql_param,
    sub
    {
        my ($pk,$cdn_downstream_server,$cp_member) = @_;
        # Compute the list of keys that must be distributed to this server.

        our $key_content = '';
        
        sub add_key($)
        {
            my $key_pk = shift;
            my $key_ip = $self->primary_ip($key_pk);
            my $file = CCNCore::Home::config."/system/$key_ip/id_rsa.pub";
            $self->error("Non-existent public key $file, has server $key_ip been bootstrapped?")
                unless -e $file;
            $key_content .= content_of($file);
        }
        
        # 1. All servers in the system (the manager included) should have the public key of the manager as an authorized key.
        add_key($manager_pk);
        
        # 2. All servers in the system should have the public key of their cdn_downstream_server as an authorized key.
        add_key($cdn_downstream_server) if defined $cdn_downstream_server;
        
        # 3. All cp_member servers inside a voicemail or menu cluster should have the public key of all the other servers in that cluster.
        if( defined $cp_member )
        {
            $self->sql_do_for( q(SELECT pk FROM server WHERE cp_member = ? AND cp_member IN (SELECT pk FROM cluster_needs_replication);), $cp_member,
            sub
            {
                my $other_pk = shift;
                add_key($other_pk) unless $other_pk == $pk; 
            });
        }
        
        # 4. All GUI servers must be able to replicate themes, etc.
        if( $self->p->is_a_gui_node )
        {
            $self->sql_do_for( q(SELECT server FROM provisioning_servers),
            sub
            {
                my $other_pk = shift;
                add_key($other_pk) unless $other_pk == $pk; 
            });
        }

        # 5. All CDN servers trust the base distributor systems.
        if( $self->sql_get('SELECT COUNT(*) FROM server WHERE cdn_downstream_server = ? OR cdn_upstream_server = ?',$pk,$pk) > 0 )
        {
            $self->sql_do_for( q(SELECT ssh_pubkey FROM provisioning_servers WHERE ssh_pubkey IS NOT NULL),
            sub
            {
                my $pub_key = shift;
                $key_content .= CCN::Text::hex_to_text($pub_key);
            });
        }

        my $ip = $self->primary_ip($pk);

        mkdir CCNCore::Home::config."/system/$ip";
        print_to(CCNCore::Home::config."/system/$ip/authorized_keys2",$key_content);

        # Handle the manager differently (we may not have an ssh key to remotely access it).
        if( $pk eq $self->p->server_pk )
        {
            $spool->spool_request("merge-keys $ip ".$t->ticket);
            $count++;
        }
        else
        {
            $spool->spool_request("send-keys $ip ".$t->ticket);
            $count++;
        }
        
    });

    # 'wait' will:
    # Retrieve the callback information from the local ticket (on the manager server)
    # Send the completion information back to the caller (the GUI node)
    $t->add_action($count,'wait');
}
1;